use bytes::Bytes;

/// RESP (Redis Serialization Protocol) data types
#[derive(Debug, Clone, PartialEq)]
pub enum RespValue {
    /// Simple string: +OK\r\n
    SimpleString(String),

    /// Error: -Error message\r\n
    Error(String),

    /// Integer: :1000\r\n
    Integer(i64),

    /// Bulk string: $6\r\nfoobar\r\n or $-1\r\n for null
    BulkString(Option<Bytes>),

    /// Array: *2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n or *-1\r\n for null
    Array(Option<Vec<RespValue>>),
}

impl RespValue {
    /// Create a simple string
    pub fn simple_string<S: Into<String>>(s: S) -> Self {
        RespValue::SimpleString(s.into())
    }

    /// Create an error
    pub fn error<S: Into<String>>(s: S) -> Self {
        RespValue::Error(s.into())
    }

    /// Create an integer
    pub fn integer(i: i64) -> Self {
        RespValue::Integer(i)
    }

    /// Create a bulk string
    pub fn bulk_string<B: Into<Bytes>>(b: B) -> Self {
        RespValue::BulkString(Some(b.into()))
    }

    /// Create a null bulk string
    pub fn null_bulk_string() -> Self {
        RespValue::BulkString(None)
    }

    /// Create an array
    pub fn array(arr: Vec<RespValue>) -> Self {
        RespValue::Array(Some(arr))
    }

    /// Create a null array
    pub fn null_array() -> Self {
        RespValue::Array(None)
    }

    /// Create OK response
    pub fn ok() -> Self {
        RespValue::simple_string("OK")
    }

    /// Create PONG response
    pub fn pong() -> Self {
        RespValue::simple_string("PONG")
    }

    /// Convert to string if possible
    pub fn as_string(&self) -> Option<String> {
        match self {
            RespValue::SimpleString(s) => Some(s.clone()),
            RespValue::BulkString(Some(bytes)) => {
                String::from_utf8(bytes.to_vec()).ok()
            }
            _ => None,
        }
    }

    /// Convert to integer if possible
    pub fn as_integer(&self) -> Option<i64> {
        match self {
            RespValue::Integer(i) => Some(*i),
            RespValue::SimpleString(s) => s.parse().ok(),
            RespValue::BulkString(Some(bytes)) if !bytes.is_empty() => {
                let s = std::str::from_utf8(bytes).ok()?;
                s.parse().ok()
            }
            _ => None,
        }
    }

    /// Convert to array if possible
    pub fn as_array(&self) -> Option<&Vec<RespValue>> {
        match self {
            RespValue::Array(Some(arr)) => Some(arr),
            _ => None,
        }
    }
}

impl From<String> for RespValue {
    fn from(s: String) -> Self {
        RespValue::bulk_string(s)
    }
}

impl From<&str> for RespValue {
    fn from(s: &str) -> Self {
        RespValue::bulk_string(s.to_string())
    }
}

impl From<i64> for RespValue {
    fn from(i: i64) -> Self {
        RespValue::integer(i)
    }
}

impl From<Vec<RespValue>> for RespValue {
    fn from(arr: Vec<RespValue>) -> Self {
        RespValue::array(arr)
    }
}